package com.aqie.haikang.dorm.crontab;

import com.aqie.haikang.dorm.entity.Cron;
import com.aqie.haikang.dorm.service.IHkEnterService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.util.StringUtils;

import javax.annotation.PreDestroy;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;

/**
 * @Author aqie
 * @Date 2021/12/11 13:46
 * @desc 2. 动态基于接口
 * [数据库动态执行多个定时任务](https://blog.csdn.net/qq_39389954/article/details/109053740)
 * [cron表达式](https://www.cnblogs.com/yanghj010/p/10875151.html)
 */
@Slf4j
@Configuration
@EnableScheduling
public class DynamicScheduleTask implements SchedulingConfigurer {
    @Autowired
    private IHkEnterService hkEnterService;

    private volatile ScheduledTaskRegistrar registrar;
    private final ConcurrentHashMap<Integer, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<Integer, CronTask> cronTasks = new ConcurrentHashMap<>();



    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        // 默认30个线程
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(30));
        this.registrar = taskRegistrar;
    }

    /**
     * 刷新任务
     * @param crons
     */
    public void refresh(List<Cron> crons) {
        // 取消已删除的策略任务
        Set<Integer> sids = scheduledFutures.keySet();
        log.info("keySet {}", sids);
        for (Integer sid : sids) {
            if(!exists(crons, sid)){
                log.info("不存在 {}", sid);
                scheduledFutures.get(sid).cancel(false);
            }
        }

        if(crons != null){
            log.info("refresh {}", crons.size());
            for (Cron cron : crons) {
                String expression = cron.getCron();
                Integer cronId = cron.getId();
                // 计划任务表达式为空跳过
                if(StringUtils.isEmpty(expression)){
                    log.info("计划任务表达式为空 {}", cronId);
                    continue;
                }
                // 计划任务存在且表达式未发生变化则跳过
                if(scheduledFutures.containsKey(cronId) &&
                        expression.equals(cronTasks.get(cronId).getExpression())){
                    log.info("计划任务存在且表达式未发生变化则跳过 {}", cronId);
                    continue;
                }
                // 如果策略执行时间发生变化 则取消当前策略任务
                if(scheduledFutures.containsKey(cronId)){
                    scheduledFutures.get(cronId).cancel(false);
                    scheduledFutures.remove(cronId);
                    cronTasks.remove(cronId);
                    log.info("策略执行时间发生变化 则取消 {}", cronId);
                }
                CronTask task = new CronTask(new Runnable() {
                    @Override
                    public void run() {
                        log.info("{} 执行动态定时任务: {}",cron.getName(), LocalDateTime.now().toLocalTime());
                        hkEnterService.postData();
                    }
                }, expression);
                ScheduledFuture<?> future = registrar.getScheduler().schedule(task.getRunnable(), task.getTrigger());
                cronTasks.put(cronId, task);
                log.info("添加任务 {}", cronId);
                scheduledFutures.put(cronId, future);
            }
        }
    }

    private boolean exists(List<Cron> crons, Integer id) {
        for (Cron cron:crons) {
            if (cron.getId().equals(id)) {
                return true;
            }
        }
        return false;
    }

    @PreDestroy
    public void destroy() {
        this.registrar.destroy();
    }
}
